/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.objects.text;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import org.easyway.interfaces.base.IDestroyable;
import org.easyway.interfaces.sprites.IFont;
import org.easyway.objects.texture.TextureCompact;
import org.easyway.system.StaticRef;
import org.easyway.system.state.OpenGLState;
import org.easyway.utils.Utility;
import org.lwjgl.opengl.GL11;

public class EWStaticFont implements IFont, IDestroyable {

	static final long serialVersionUID = 22;

	private static final Color OPAQUE_WHITE = new Color(0xFFFFFFFF, true);

	private static final Color TRANSPARENT_BLACK = new Color(0x00000000, true);

	private TextureCompact texture;

	private int base; // Base Display List For The Font Set

	private int size;

	private IntBuffer textureBuffer;

	private FontMetrics fontMatric;

	private int internalFontSize;

	private float scalar; // size / internalFontSize;

	private boolean destroyed = false;

	// ------------------------------ COSTRUTTORE -----------------

	public EWStaticFont(String fontName, int size) {
		Font font; // Font object
		this.size = size;
		if (fontName == null)
			fontName = "Courier New";
		BufferedImage fontImage; // image for creating the bitmap
		int bitmapSize = 512; // set the size for the bitmap
		// texture
		boolean sizeFound = false;
		boolean directionSet = false;
		int delta = 0;
		int fontSize = 24;

		// ----- size of image --------
		while (!sizeFound) {
			font = new Font(fontName, Font.PLAIN, fontSize); // Font Name
			// use BufferedImage.TYPE_4BYTE_ABGR to allow alpha blending
			fontImage = new BufferedImage(bitmapSize, bitmapSize,
					BufferedImage.TYPE_4BYTE_ABGR);
			Graphics2D g = (Graphics2D) fontImage.getGraphics();
			g.setFont(font);
			FontMetrics fm = g.getFontMetrics();
			int width = fm.stringWidth("W");
			int height = fm.getHeight();
			int lineWidth = (width > height) ? width * 16 : height * 16;
			if (!directionSet) {
				if (lineWidth > bitmapSize) {
					delta = -2;
				} else {
					delta = 2;
				}
				directionSet = true;
			}
			if (delta > 0) {
				if (lineWidth < bitmapSize) {
					fontSize += delta;
				} else {
					sizeFound = true;
					fontSize -= delta;
				}
			} else if (delta < 0) {
				if (lineWidth > bitmapSize) {
					fontSize += delta;
				} else {
					sizeFound = true;
					fontSize -= delta;
				}
			}
		}

		/*
		 * Now that a font size has been determined, create the final image, set
		 * the font and draw the standard/extended ASCII character set for that
		 * font.
		 */
		font = new Font(fontName, Font.BOLD, fontSize); // Font Name
		// use BufferedImage.TYPE_4BYTE_ABGR to allow alpha blending
		fontImage = new BufferedImage(bitmapSize, bitmapSize,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) fontImage.getGraphics();
		g.setFont(font);
		g.setColor(OPAQUE_WHITE);
		g.setBackground(TRANSPARENT_BLACK);
		FontMetrics fm = g.getFontMetrics();
		fontMatric = fm;
		internalFontSize = fontSize;
		for (int i = 0; i < 256; i++) {
			int x = i % 16;
			int y = i / 16;
			// TODO: DA RIVEDERE che fa CAGARE
			char ch[] = { (char) i };
			String temp = new String(ch);
			g.drawString(temp, (x * 32) + 1, (y * 32) + fm.getAscent());
		}

		// Flip Image
		AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
		tx.translate(0, -fontImage.getHeight(null));
		AffineTransformOp op = new AffineTransformOp(tx,
				AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		fontImage = op.filter(fontImage, null);

		// Put Image In Memory
		ByteBuffer scratch = ByteBuffer.allocateDirect(4 * fontImage.getWidth()
				* fontImage.getHeight());

		byte data[] = (byte[]) fontImage.getRaster().getDataElements(0, 0,
				fontImage.getWidth(), fontImage.getHeight(), null);
		scratch.clear();
		scratch.put(data);
		scratch.rewind();

		texture = new TextureCompact();
		texture.width = fontImage.getWidth();
		texture.height = fontImage.getHeight();
		texture.widthHW = fontImage.getWidth();
		texture.heightHW = fontImage.getHeight();
		texture.isRegular = true;
		texture.isSolid = false;

		// Create A IntBuffer For Image Address In Memory
		textureBuffer = ByteBuffer.allocateDirect(4).order(
				ByteOrder.nativeOrder()).asIntBuffer();
		GL11.glGenTextures(textureBuffer); // Create Texture In OpenGL

		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureBuffer.get(0));
		// Typical Texture Generation Using Data From The Image

		// Linear Filtering
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
				GL11.GL_LINEAR);
		// Linear Filtering
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
				GL11.GL_LINEAR);
		// Generate The Texture
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, fontImage
				.getWidth(), fontImage.getHeight(), 0, GL11.GL_RGBA,
				GL11.GL_UNSIGNED_BYTE, scratch);

		texture.texture = textureBuffer.get(0); // Return Image Address In
		// Memory
		texture.textureBuffer = textureBuffer;

		base = GL11.glGenLists(256); // Storage For 256 Characters

		float textureDelta = 1.0f / 16.0f;
		for (int i = 0; i < 256; i++) {
			float u = ((float) (i % 16)) / 16.0f;
			float v = 1.f - (((float) (i / 16)) / 16.0f);
			GL11.glNewList(base + i, GL11.GL_COMPILE);
			texture.bind();
			GL11.glBegin(GL11.GL_QUADS);

			GL11.glTexCoord2f(u, v);
			GL11.glVertex3f(0, 0, 0);
			GL11.glTexCoord2f((u + textureDelta), v);
			GL11.glVertex3f(size, 0, 0);
			GL11.glTexCoord2f((u + textureDelta), v - textureDelta);
			GL11.glVertex3f(size, size, 0);
			GL11.glTexCoord2f(u, v - textureDelta);
			GL11.glVertex3f(0, size, 0);
			GL11.glEnd();
			GL11.glEndList();
		}
		scalar = (float) size / (float) internalFontSize;
	}

	// ------------------------------ WRITING ROUTINES -----------------

	public void setSize(int size) {
		// Utility.error("you can't resize a EWStaticFont; use a EWFont!",
		// "EWStaticFont.setSize(int)");
	}

	public int getSize() {
		return size;
	}

	public int getLenght(char character) {
		return (int) scalar * fontMatric.charWidth(character);
	}

	public int getLenght(String string) {
		return (int) scalar * fontMatric.stringWidth(string);
	}

	// ------------------------------ WRITING ROUTINES -----------------

	public void writeFPS() {
		int fps = StaticRef.core.getFps();
		String t;
		if (fps > 60)
			t = EWText.createColor(0, 255, 0);
		else if (fps < 50)
			t = EWText.createColor(255, 0, 0);
		else
			t = EWText.createColor(255, 255, 0);
		GL11.glColor3f(1, 1, 1);
		writeString(t = ("FPS: " + t + fps), t.length(), 0, 0);
	}

	/**
	 * draws a char on screen<br>
	 * At coordinates 0,0
	 */
	public void drawChar(int mchar) {
		GL11.glCallList(base + mchar);
		// float textureDelta = 0.0625f; // 1.0f / 16.0f;
		// float u = ((float) (mchar % 16)) * textureDelta; // / 16;
		// float v = 1.f - (((float) ((int) (mchar * textureDelta))) *
		// textureDelta);
		// texture.bind();
		// GL11.glBegin(GL11.GL_QUADS);
		// GL11.glTexCoord2f(u, v);
		// GL11.glVertex3f(0, 0, 0);
		// GL11.glTexCoord2f((u + textureDelta), v);
		// GL11.glVertex3f(size, 0, 0);
		// GL11.glTexCoord2f((u + textureDelta), v - textureDelta);
		// GL11.glVertex3f(size, size, 0);
		// GL11.glTexCoord2f(u, v - textureDelta);
		// GL11.glVertex3f(0, size, 0);
		// GL11.glEnd();
	}

	public void writeString(String msg, int lenght, int x, int y) { // Custom
		if (msg == null)
			return;
		lenght = Math.min(msg.length(), lenght);
		OpenGLState.disableBlending();
		GL11.glLoadIdentity();
		GL11.glTranslatef(x, y, 0f);
		texture.bind();
		GL11.glEnable(GL11.GL_ALPHA_TEST);
		for (int i = 0; i < lenght; i++) {
			if (msg.charAt(i) == '%' && i != msg.length() - 1
					&& msg.charAt(i + 1) != '%') {
				try {
					i++;
					byte red = (byte) msg.charAt(i);
					i++;
					byte green = (byte) msg.charAt(i);
					i++;
					byte blue = (byte) msg.charAt(i);
					GL11.glColor4ub(red, green, blue, (byte) 255);
					i++;
				} catch (Exception e) {
					Utility.error("uso inappropriato del carattere %",
							"EWFont.writeString", e);
					continue;
				}
			} else if (msg.charAt(i) == '\n') {
				Utility.error("uso inappropriato del carattere \\n",
						"EWFont.writeString");
			}
			GL11.glCallList(base + msg.charAt(i));
			GL11.glTranslatef((int) (scalar * fontMatric.stringWidth(""
					+ msg.charAt(i))), 0, 0);
		}
		GL11.glLoadIdentity();
	}

	public void writeStringRect(String msg, int lenght, int x, int y, int width) {
		int position = 0;
		int line = 0;

		if (msg == null)
			return;
		lenght = Math.min(msg.length(), lenght);
		OpenGLState.disableBlending();
		GL11.glLoadIdentity();
		GL11.glTranslatef(x, y, 0f);
		texture.bind();
		for (int i = 0; i < lenght; i++) {
			if (msg.charAt(i) == '%' && i != msg.length() - 1
					&& msg.charAt(i + 1) != '%') {
				try {
					i++;
					byte red = (byte) msg.charAt(i);
					i++;
					byte green = (byte) msg.charAt(i);
					i++;
					byte blue = (byte) msg.charAt(i);
					GL11.glColor4ub(red, green, blue, (byte) 255);
					i++;
				} catch (Exception e) {
					Utility.error("uso inappropriato del carattere %",
							"EWFont.writeStringRect", e);
					continue;
				}
			} else if (msg.charAt(i) == '\n') {
				position = width;
			}

			GL11.glCallList(base + msg.charAt(i));
			position += scalar * fontMatric.stringWidth("" + msg.charAt(i));

			if (position >= width) {
				GL11.glLoadIdentity();
				line += size;
				GL11.glTranslatef(x, y + line, 0);
				position = 0;
				if (i + 1 < msg.length() && msg.charAt(i + 1) == ' ')
					++i;
			} else {
				GL11.glTranslatef((int) (scalar * fontMatric.stringWidth(""
						+ msg.charAt(i))), 0, 0);
			}
		}
		GL11.glLoadIdentity();
	}

	// --------------------- DESTROY -----------------------

	public void finalize() {
		destroy();
	}

	public void destroy() {
		if (destroyed)
			return;
		GL11.glDeleteLists(base, 256);
		if (textureBuffer != null)
			GL11.glDeleteTextures(textureBuffer);
	}

	public boolean isDestroyed() {
		return destroyed;
	}

	public boolean canChangeSize() {
		return false;
	}

}
